#ifndef IVP_SOLVER_FACTORY_HPP
#define IVP_SOLVER_FACTORY_HPP

#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <string>

#include "IVPSolver.hpp"
// LMM方法
#include "AdamsBashforth.hpp"
#include "AdamsMoulton.hpp"
#include "BDF.hpp"
// RK方法
#include "ClassicalRK.hpp"
#include "ESDIRK.hpp"
#include "GLRK.hpp"
// 嵌入式RK方法
#include "DormandPrince.hpp"
#include "Fehlberg.hpp"

class IVPSolverFactory {
public:
  // 通过函数绑定支持更多参数类型和构造函数签名
  using CreateIVPSolverCallback = std::function<std::unique_ptr<IVPSolver>(
      int dim, double dt, double t0, double t1)>;

private:
  using CallbackMap = std::map<std::string, CreateIVPSolverCallback>;

public:
  void registerIVPSolver(const std::string &ID,
                         CreateIVPSolverCallback createFn) {
    callbacks_[ID] = createFn;
  }

  std::unique_ptr<IVPSolver> createIVPSolver(const std::string &ID, int dim,
                                             double dt, double t0, double t1) {
    if (!callbacks_.count(ID)) {
      std::cerr << "IVPSolver:: No such IVP Solver called '" << ID << "'."
                << std::endl;
      return nullptr;
    }
    return callbacks_[ID](dim, dt, t0, t1);
  }

private:
  CallbackMap callbacks_;
  std::map<std::string, std::string> solverDescriptions_; // 存储求解器描述、

private:
  IVPSolverFactory() = default;
  IVPSolverFactory(const IVPSolverFactory &) = delete; // 防止复制
  IVPSolverFactory &operator=(const IVPSolverFactory &) = delete; // 防止赋值
  ~IVPSolverFactory() = default;

public:
  static IVPSolverFactory &getInstance() {
    static IVPSolverFactory instance;
    return instance;
  }

public:
  // 获取所有已注册求解器的名称
  std::vector<std::string> getRegisteredSolvers() const {
    std::vector<std::string> solvers;
    for (const auto &pair : callbacks_) {
      solvers.push_back(pair.first);
    }
    return solvers;
  }

  // 获取已注册求解器的数量
  size_t getNumRegisteredSolvers() const { return callbacks_.size(); }

  // 检查求解器是否已注册
  bool isSolverRegistered(const std::string &name) const {
    return callbacks_.count(name) > 0;
  }

  // 为求解器添加描述信息
  void setSolverDescription(const std::string &name,
                            const std::string &description) {
    if (isSolverRegistered(name)) {
      solverDescriptions_[name] = description;
    }
  }

  // 获取求解器的描述信息
  std::string getSolverDescription(const std::string &name) const {
    if (solverDescriptions_.count(name) > 0) {
      return solverDescriptions_.at(name);
    }
    return "No description available.";
  }
};

/**
 * 注册所有求解器
 * 为每种方法添加回调函数，使用lambda以支持不同的构造函数参数
 */
inline void RegisterAllIVPSolvers() {
  auto &factory = IVPSolverFactory::getInstance();

  // Linear Multistep Methods (LMM)
  factory.registerIVPSolver("BDF[1]",
                            [](int dim, double dt, double t0,
                               double t1) -> std::unique_ptr<IVPSolver> {
                              return std::make_unique<BDF>(dim, dt, t0, t1, 1);
                            });
  factory.setSolverDescription("BDF[1]",
                               "1-order Backward Differentiation Formula");

  factory.registerIVPSolver("BDF[2]",
                            [](int dim, double dt, double t0,
                               double t1) -> std::unique_ptr<IVPSolver> {
                              return std::make_unique<BDF>(dim, dt, t0, t1, 2);
                            });
  factory.setSolverDescription("BDF[2]",
                               "2-order Backward Differentiation Formula");

  factory.registerIVPSolver("BDF[3]",
                            [](int dim, double dt, double t0,
                               double t1) -> std::unique_ptr<IVPSolver> {
                              return std::make_unique<BDF>(dim, dt, t0, t1, 3);
                            });
  factory.setSolverDescription("BDF[3]",
                               "3-order Backward Differentiation Formula");

  factory.registerIVPSolver("BDF[4]",
                            [](int dim, double dt, double t0,
                               double t1) -> std::unique_ptr<IVPSolver> {
                              return std::make_unique<BDF>(dim, dt, t0, t1, 4);
                            });
  factory.setSolverDescription("BDF[4]",
                               "4-order Backward Differentiation Formula");

  factory.registerIVPSolver("AdamsMoulton[2]",
                            [](int dim, double dt, double t0,
                               double t1) -> std::unique_ptr<IVPSolver> {
                              return std::make_unique<AdamsMoulton>(dim, dt, t0,
                                                                    t1, 2);
                            });
  factory.setSolverDescription("AdamsMoulton[2]", "2-order Adams-Moulton");

  factory.registerIVPSolver("AdamsMoulton[3]",
                            [](int dim, double dt, double t0,
                               double t1) -> std::unique_ptr<IVPSolver> {
                              return std::make_unique<AdamsMoulton>(dim, dt, t0,
                                                                    t1, 3);
                            });
  factory.setSolverDescription("AdamsMoulton[3]", "3-order Adams-Moulton");

  factory.registerIVPSolver("AdamsMoulton[4]",
                            [](int dim, double dt, double t0,
                               double t1) -> std::unique_ptr<IVPSolver> {
                              return std::make_unique<AdamsMoulton>(dim, dt, t0,
                                                                    t1, 4);
                            });
  factory.setSolverDescription("AdamsMoulton[4]", "4-order Adams-Moulton");

  factory.registerIVPSolver("AdamsMoulton[5]",
                            [](int dim, double dt, double t0,
                               double t1) -> std::unique_ptr<IVPSolver> {
                              return std::make_unique<AdamsMoulton>(dim, dt, t0,
                                                                    t1, 5);
                            });
  factory.setSolverDescription("AdamsMoulton[5]", "5-order Adams-Moulton");

  factory.registerIVPSolver("AdamsBashforth[1]",
                            [](int dim, double dt, double t0,
                               double t1) -> std::unique_ptr<IVPSolver> {
                              return std::make_unique<AdamsBashforth>(
                                  dim, dt, t0, t1, 1);
                            });
  factory.setSolverDescription("AdamsBashforth[1]", "1-");

  factory.registerIVPSolver("AdamsBashforth[2]",
                            [](int dim, double dt, double t0,
                               double t1) -> std::unique_ptr<IVPSolver> {
                              return std::make_unique<AdamsBashforth>(
                                  dim, dt, t0, t1, 2);
                            });
  factory.setSolverDescription("AdamsBashforth[2]",
                               "2-order Adams-Bashforth");

  factory.registerIVPSolver("AdamsBashforth[3]",
                            [](int dim, double dt, double t0,
                               double t1) -> std::unique_ptr<IVPSolver> {
                              return std::make_unique<AdamsBashforth>(
                                  dim, dt, t0, t1, 3);
                            });
  factory.setSolverDescription("AdamsBashforth[3]",
                               "3-order Adams-Bashforth");

  factory.registerIVPSolver("AdamsBashforth[4]",
                            [](int dim, double dt, double t0,
                               double t1) -> std::unique_ptr<IVPSolver> {
                              return std::make_unique<AdamsBashforth>(
                                  dim, dt, t0, t1, 4);
                            });
  factory.setSolverDescription("AdamsBashforth[4]",
                               "4-order Adams-Bashforth");

  // Runge-Kutta Methods (RK)
  factory.registerIVPSolver("ClassicalRK",
                            [](int dim, double dt, double t0,
                               double t1) -> std::unique_ptr<IVPSolver> {
                              return std::make_unique<ClassicalRK>(dim, dt, t0,
                                                                   t1);
                            });
  factory.setSolverDescription("ClassicalRK", "classical RK method of order 4");

  factory.registerIVPSolver("ESDIRK",
                            [](int dim, double dt, double t0,
                               double t1) -> std::unique_ptr<IVPSolver> {
                              return std::make_unique<ESDIRK>(dim, dt, t0, t1);
                            });
  factory.setSolverDescription("ESDIRK", "ESDIRK method of order 4");

  factory.registerIVPSolver("GLRK[1]",
                            [](int dim, double dt, double t0,
                               double t1) -> std::unique_ptr<IVPSolver> {
                              return std::make_unique<GLRK>(dim, dt, t0, t1, 1);
                            });
  factory.setSolverDescription("GLRK[1]", "1-step GLRK method of order 2");

  factory.registerIVPSolver("GLRK[2]",
                            [](int dim, double dt, double t0,
                               double t1) -> std::unique_ptr<IVPSolver> {
                              return std::make_unique<GLRK>(dim, dt, t0, t1, 2);
                            });
  factory.setSolverDescription("GLRK[2]", "2-step GLRK method of order 4");

  factory.registerIVPSolver("GLRK[3]",
                            [](int dim, double dt, double t0,
                               double t1) -> std::unique_ptr<IVPSolver> {
                              return std::make_unique<GLRK>(dim, dt, t0, t1, 3);
                            });
  factory.setSolverDescription("GLRK[3]", "3-step GLRK method of order 6");

  // Embedded Runge-Kutta Methods
  factory.registerIVPSolver("Fehlberg",
                            [](int dim, double dt, double t0,
                               double t1) -> std::unique_ptr<IVPSolver> {
                              return std::make_unique<Fehlberg>(dim, dt, t0,
                                                                t1);
                            });
  factory.setSolverDescription("Fehlberg",
                               "Fehlberg method of order 4(5) with embedded");

  factory.registerIVPSolver("DormandPrince",
                            [](int dim, double dt, double t0,
                               double t1) -> std::unique_ptr<IVPSolver> {
                              return std::make_unique<DormandPrince>(dim, dt,
                                                                     t0, t1);
                            });
  factory.setSolverDescription("DormandPrince",
                               "Dormand-Prince method of order 5(4) with embedded");
}

#endif // IVP_SOLVER_FACTORY_HPP